package main

import (
    "sync"
    "fmt"
    "bytes"
    "time"
)

type Metric struct {
    mu       sync.RWMutex
    ErrCount map[string]map[string]int64 //action error
    Count    map[string]map[int]int64    //action code
    Duration map[string]map[int]int64    //action code
}

func NewMetric() *Metric {
    return &Metric{
        ErrCount: make(map[string]map[string]int64),
        Count:    make(map[string]map[int]int64),
        Duration: make(map[string]map[int]int64),
    }
}

func (m *Metric) bytes() []byte {
    buf := new(bytes.Buffer)
    m.mu.RLock()
    defer m.mu.RUnlock()
    buf.WriteString("# TYPE req_count counter\n")
    for act, m := range m.Count {
        for code, v := range m {
            s := fmt.Sprintf(`req_count{action="%s",code="%d"} %d %d\n`, act, code, v, time.Now().UnixNano()/1000/1000)
            buf.WriteString(s)
        }
    }
    buf.WriteString("# TYPE error_count counter\n")
    for act, m := range m.ErrCount {
        for err, v := range m {
            s := fmt.Sprintf(`error_count{action="%s",err="%s"} %d %d\n`, act, err, v, time.Now().UnixNano()/1000/1000)
            buf.WriteString(s)
        }
    }
    buf.WriteString("# TYPE req_duration counter\n")
    for act, m := range m.Duration {
        for code, v := range m {
            s := fmt.Sprintf(`req_duration{action="%s",code="%d"} %d %d\n`, act, code, v, time.Now().UnixNano()/1000/1000)
            buf.WriteString(s)
        }
    }
    return buf.Bytes()
}

func (m *Metric) Add(ret *Result) {
    m.mu.Lock()
    defer m.mu.Unlock()
    act := string(ret.action)
    if _, ok := m.Count[act]; !ok {
        m.Count[act] = make(map[int]int64)
    }
    if _, ok := m.Duration[act]; !ok {
        m.Duration[act] = make(map[int]int64)
    }
    if _, ok := m.ErrCount[act]; !ok {
        m.ErrCount[act] = make(map[string]int64)
    }
    m.Count[act][ret.code] += 1
    m.Count[act][ret.code] += int64(m.Duration)
    if err := ret.err; ret.err != nil {
        m.ErrCount[act][err.Error()] += 1
    }
}
